// 条款46：需要类型转换时请为模板定义非成员函数
// 核心问题
// 这条款主要解决的是隐式类型转换在模板类中的应用问题。当我们需要支持混合类型运算（如有理数类与整数相乘）时，
// 如果将运算符重载实现为成员函数，会导致隐式类型转换无法正常工作。

// 详细解释
// 假设我们有一个表示有理数的模板类：
template <typename T>
class Rational
{
public:
    Rational(const T &numerator = 0, const T &denominator = 1);

    // 其他成员函数...

    // 成员函数形式的乘法运算符
    const Rational operator*(const Rational &rhs) const;
};

// 如果我们尝试进行如下操作：
Rational<int> oneHalf(1, 2);
Rational<int> result = oneHalf * 2; // 尝试将有理数与整数相乘
// 这段代码会编译失败。原因是：成员函数形式的运算符要求左操作数必须是该类的对象，而整数2不是Rational<int>类型，
// 编译器会尝试将2转换为Rational<int>，但这种隐式转换只适用于非模板类。
// operator*优先于隐式类型转换，所以会失败

// 解决方案
// 将运算符重载实现为非成员函数：
template <typename T>
class Rational
{
public:
    Rational(const T &numerator = 0, const T &denominator = 1);

    // 其他成员函数...

    // 获取分子分母的访问函数
    const T &numerator() const;
    const T &denominator() const;
};

// 非成员函数形式的乘法运算符
template <typename T>
const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs)
{
    return Rational<T>(lhs.numerator() * rhs.numerator(),
                       lhs.denominator() * rhs.denominator());
}
// 但这仍然不能解决混合类型运算的问题。对于Rational<int> * 2，编译器无法推导出T的类型。

// 最佳实践
// 为了支持混合类型运算，我们需要在类内声明一个友元函数模板：
template <typename T>
class Rational
{
public:
    Rational(const T &numerator = 0, const T &denominator = 1);

    // 其他成员函数...

    const T &numerator() const;
    const T &denominator() const;

    // 在类内声明友元函数模板
    template <typename U>
    friend const Rational<U> operator*(const Rational<U> &lhs, const Rational<U> &rhs);
};

// 在类外实现友元函数模板
template <typename T>
const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs)
{
    return Rational<T>(lhs.numerator() * rhs.numerator(),
                       lhs.denominator() * rhs.denominator());
}